package cn.quang.search.admin.service.impl;

import cn.quang.search.admin.entity.dto.Operator;
import cn.quang.search.admin.entity.dto.Result;
import cn.quang.search.admin.entity.po.EntityLogicTable;
import cn.quang.search.admin.entity.po.EntityLogicTableRelation;
import cn.quang.search.admin.entity.po.EntityTable;
import cn.quang.search.admin.entity.vo.meta.ColumnVO;
import cn.quang.search.admin.entity.vo.meta.TableVO;
import cn.quang.search.admin.entity.vo.table.TableConfigVO;
import cn.quang.search.admin.entity.vo.table.TableDataSourceTreeVO;
import cn.quang.search.admin.entity.vo.table.TableRequest;
import cn.quang.search.admin.mapper.EntityLogicTableMapper;
import cn.quang.search.admin.mapper.EntityLogicTableRelationMapper;
import cn.quang.search.admin.mapper.EntityTableMapper;
import cn.quang.search.admin.service.IDBMetaService;
import cn.quang.search.admin.service.IEntityTableService;
import cn.quang.search.admin.service.ISearchConfigChangeService;
import cn.quang.search.admin.service.common.RedisService;
import cn.quang.search.admin.utils.AssertUtils;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * @author:  Entity Generator
 * @since:  2024-02-28 10:57:07
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class EntityTableService implements IEntityTableService {

	private final EntityTableMapper entityTableMapper;
	private final EntityLogicTableMapper entityLogicTableMapper;
	private final EntityLogicTableRelationMapper entityLogicTableRelationMapper;
	
	private final IDBMetaService dbMetaService;
	private final ISearchConfigChangeService searchConfigChangeService;
	private final RedisService redisService;

	private static final String LOCK_KEY = "CRM3$ENTITY_TABLE$";
	private static final int EXPIRE_SECONDS = 60;

	/**
	 * 根据数据源ID读取针对该数据源的表配置信息
	 */
	@Override
	public Result<TableConfigVO> tableConfigurations(long dataSourceId, String cookie){
		Result<TableConfigVO> methodResult = new Result<>();
		// 已配置的表
		List<EntityTable> entityTables = entityTableMapper.selectByDataSourceId(dataSourceId);
		Map<String, EntityTable> entityTableMap = entityTables.stream().collect(Collectors.toMap(EntityTable::getTableName, v -> v));
		// 全部数据源表
		Result<List<TableVO>> tablesResult = dbMetaService.getTables(dataSourceId, cookie);
		if(tablesResult.isFail()) {
			return methodResult.changeErr("读取数据源下所有的表失败：" + tablesResult.getMessage());
		}
		List<TableVO> tables = tablesResult.getTag();
		List<TableVO> selectedTables = Lists.newArrayList();
		List<TableVO> notSelectTables = Lists.newArrayList();
		for (TableVO table : tables) {
			if(entityTableMap.containsKey(table.getTableName())) {
				EntityTable entityTable = entityTableMap.get(table.getTableName());
				TableVO tableVO = TableVO.of(entityTable.getTableName(), entityTable.getName());
				selectedTables.add(tableVO);
			} else {
				notSelectTables.add(table);
			}
		}
		TableConfigVO vo = new TableConfigVO();
		vo.setSelectedTables(selectedTables);
		vo.setNotSelectTables(notSelectTables);
		return methodResult.changeSucc(vo);
	}

	/**
	 * 数据源下的表
	 * @param operator 操作人
	 * @param request 表名称
	 * @return 添加结果
	 */
	@Override
	public Result<?> addOrUpdate(Operator operator, TableRequest request){
		Result<?> methodResult = new Result<>();
		long dataSourceId = request.getDataSourceId();
		AssertUtils.argumentNegativeOrZero(dataSourceId, "必须提供数据源");

		List<TableRequest.TableNameRequest> tableNames = request.getTableNames();
		if(tableNames == null) {
			// 支持前端传递空数组，以清空该数据源下的所有表
			tableNames = Lists.newArrayList();
		}

		Date now = new Date();

		// 存储增删改的表名称，用于输出日志
		List<String> newTableNames = Lists.newArrayList();
		List<String> modifyTableNames = Lists.newArrayList();
		List<String> deleteTableNames = Lists.newArrayList();

		String uniqueKey = LOCK_KEY + operator.getOperatorId() + "$" + dataSourceId;
		String requestId = UUID.randomUUID().toString();
		if(redisService.tryLock(uniqueKey, requestId, EXPIRE_SECONDS)) {
			try {
				List<EntityTable> entityTables = entityTableMapper.selectByDataSourceId(dataSourceId);
				if(entityTables == null) { entityTables = Lists.newArrayList(); }
				Map<String, EntityTable> tableNameMap = entityTables.stream().collect(Collectors.toMap(EntityTable::getTableName, v -> v));
				Map<String, TableRequest.TableNameRequest> tableRequestMap = tableNames.stream().collect(Collectors.toMap(TableRequest.TableNameRequest::getTableName, v -> v));

				List<Long> tableIds = Lists.newArrayList();
				// 执行删除
				for (EntityTable entityTable : entityTables) {
					if(!tableRequestMap.containsKey(entityTable.getTableName())){
						// 检查逻辑表是否关联了该真实表
						List<EntityLogicTableRelation> tableRelations = entityLogicTableRelationMapper.selectByTableId(entityTable.getId());
						if(CollectionUtils.isNotEmpty(tableRelations)){
							List<Long> logicTableIds = tableRelations.stream().map(EntityLogicTableRelation::getLogicTableId).distinct().collect(Collectors.toList());
							List<EntityLogicTable> logicTables = entityLogicTableMapper.selectList(new QueryWrapper<EntityLogicTable>().lambda().in(EntityLogicTable::getId, logicTableIds));
							return methodResult.changeErr("表" + String.format("%s(%s)", entityTable.getTableName(), entityTable.getName()) + "存在关联的逻辑表：" + logicTables.stream().map(EntityLogicTable::getName).collect(Collectors.joining(", ")));
						}
						tableIds.add(entityTable.getId());
						deleteTableNames.add(entityTable.getTableName());
					}
				}
				// 删除
				for (Long id : tableIds) { entityTableMapper.deleteById(id); }

				// 比较更新 ： 存在的更新，不存在的新增，未出现在请求中的删除
				for (TableRequest.TableNameRequest tableRequest : tableNames) {
					EntityTable entityTable = tableNameMap.get(tableRequest.getTableName());
					if(entityTable != null){
						// 已存在的表，执行更新
						entityTable.setName(tableRequest.getName());
						entityTable.setModifierId(operator.getOperatorId());
						entityTable.setModifyTime(now);
						entityTableMapper.updateById(entityTable);
						modifyTableNames.add(entityTable.getTableName());
					} else {
						// 新增
						entityTable = new EntityTable();
						entityTable.setName(tableRequest.getName());
						entityTable.setTableName(tableRequest.getTableName());
						entityTable.setDataSourceId(dataSourceId);
						entityTable.setCreatorId(operator.getOperatorId());
						entityTable.setCreateTime(now);
						entityTable.setModifierId(operator.getOperatorId());
						entityTable.setModifyTime(now);
						entityTableMapper.insert(entityTable);
						newTableNames.add(tableRequest.getTableName());
					}
				}

				searchConfigChangeService.triggerByDataSource(dataSourceId);
			} catch (Exception e) {
				log.error("操作失败：" + e.getMessage());
				return methodResult.changeErr("操作失败：" + e.getMessage());
			} finally {
				redisService.releaseLock(uniqueKey, requestId);
			}
		} else {
			return methodResult.changeErr("请不要频繁操作！");
		}

		log.info(String.format("新增表%s，修改表%s，删除表%s", newTableNames, modifyTableNames, deleteTableNames));
		return methodResult;
	}

	/**
	 * 校验指定的tableIds是否有同样的MetaData
	 * @param tableIds 表ID集合
	 */
	@Override
	public Result<?> isHaveSameTableMetaData(List<Long> tableIds){
		Result<?> aopResult = new Result<>();
		// 分析存在不同表结构的表，并设置到aopResult中的tag里
		try {
			List<ColumnVO> columnVos = null;
			long preTableId = 0;
			for (Long tableId : tableIds) {
				Result<List<ColumnVO>> columnsResult = dbMetaService.getColumns(tableId, StringUtils.EMPTY);
				if(columnsResult.isFail()){
					return aopResult.changeErr("表结构核验失败：" + columnsResult.getMessage());
				}
				List<ColumnVO> thisColumnVos = columnsResult.getTag();
				if(columnVos == null){
					columnVos = thisColumnVos;
					preTableId = tableId;
				}else {
					// compare
					if(columnVos.size() != thisColumnVos.size()) {
						List<EntityTable> diffTables = entityTableMapper.selectList(new QueryWrapper<EntityTable>().lambda().in(EntityTable::getId, Lists.newArrayList(preTableId, tableId)));
						return aopResult.changeErr(String.format("存在字段数量不一致的表：%s", diffTables.stream().map(input -> String.format("%s(%s)", input.getTableName(), input.getName())).collect(Collectors.joining(", "))));
					}
					Map<String, ColumnVO> columnVoMap = columnVos.stream().collect(Collectors.toMap(ColumnVO::getDic, v -> v));
					Map<String, ColumnVO> thisColumnVoMap = thisColumnVos.stream().collect(Collectors.toMap(ColumnVO::getDic, v -> v));
					if(!CollectionUtils.isEqualCollection(columnVoMap.keySet(), thisColumnVoMap.keySet())){
						List<EntityTable> diffTables = entityTableMapper.selectList(new QueryWrapper<EntityTable>().lambda().in(EntityTable::getId, Lists.newArrayList(preTableId, tableId)));
						return aopResult.changeErr(String.format("存在字段名不完全一致的表：%s", diffTables.stream().map(input -> String.format("%s(%s)", input.getTableName(), input.getName())).collect(Collectors.joining(", "))));
					}
					for (Map.Entry<String, ColumnVO> entry: columnVoMap.entrySet()) {
						ColumnVO columnVo = entry.getValue();
						ColumnVO thisColumnVo = thisColumnVoMap.get(entry.getKey());
						if(columnVo.getQueryFieldType() != thisColumnVo.getQueryFieldType()) {
							List<EntityTable> diffTables = entityTableMapper.selectList(new QueryWrapper<EntityTable>().lambda().in(EntityTable::getId, Lists.newArrayList(preTableId, tableId)));
							return aopResult.changeErr(String.format("存在字段类型(%s)不完全一致的表：%s", columnVo.getDic(), diffTables.stream().map(input -> String.format("%s(%s)", input.getTableName(), input.getName())).collect(Collectors.joining(", "))));
						}
					}
					columnVos = thisColumnVos;
					preTableId = tableId;
				}
			}
		} catch (Exception e) {
			log.error(String.format("对比表结构(%s)出现异常", tableIds), e);
			return aopResult.changeErr("对比表结构出现异常，请稍后重试！");
		}

		return aopResult;
	}

	@Override
	public Result<Map<Long, List<TableDataSourceTreeVO>>> allTable4Cascade(List<Long> dataSourceIds) {
		Result<Map<Long, List<TableDataSourceTreeVO>>> aopResult = new Result<>();
		try {
			Map<Long, List<TableDataSourceTreeVO>> result = Maps.newHashMap();
			List<EntityTable> entityTables = entityTableMapper.selectByDataSourceIds(dataSourceIds);
			if(CollectionUtils.isNotEmpty(entityTables)) {
				for (EntityTable entityTable : entityTables) {
					TableDataSourceTreeVO vo = new TableDataSourceTreeVO();
					vo.setLabel(String.format("%s(%s)", entityTable.getTableName(), entityTable.getName()));
					vo.setValue(entityTable.getId());
					vo.setLeaf(true);
					List<TableDataSourceTreeVO> tables = result.computeIfAbsent(entityTable.getDataSourceId(), key -> Lists.newArrayList());
					tables.add(vo);
				}
			}
			return aopResult.changeSucc(result);
		} catch (Exception e) {
			log.error("级联获取数据表失败", e);
			return aopResult.changeErr("级联获取数据表失败");
		}
	}
}
